package spitter.demo;

/**
 * @author yanheng
 * @data 2020/1/10 14:32
 */

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Map.Entry;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XNode {
    private final Node node;
    private final String name;
    private final String body;
    private final Properties attributes;
    private final Properties variables;
    private final XPathParser xpathParser;

    public XNode(XPathParser xpathParser, Node node, Properties variables) {
        this.xpathParser = xpathParser;
        this.node = node;
        this.name = node.getNodeName();
        this.variables = variables;
        this.attributes = this.parseAttributes(node);
        this.body = this.parseBody(node);
    }

    public XNode newXNode(Node node) {
        return new XNode(this.xpathParser, node, this.variables);
    }

    public XNode getParent() {
        Node parent = this.node.getParentNode();
        return !(parent instanceof Element) ? null : new XNode(this.xpathParser, parent, this.variables);
    }

    public String getPath() {
        StringBuilder builder = new StringBuilder();

        for(Node current = this.node; current instanceof Element; current = current.getParentNode()) {
            if (current != this.node) {
                builder.insert(0, "/");
            }

            builder.insert(0, current.getNodeName());
        }

        return builder.toString();
    }

    public String getValueBasedIdentifier() {
        StringBuilder builder = new StringBuilder();

        for(XNode current = this; current != null; current = current.getParent()) {
            if (current != this) {
                builder.insert(0, "_");
            }

            String value = current.getStringAttribute("id", current.getStringAttribute("value", current.getStringAttribute("property", (String)null)));
            if (value != null) {
                value = value.replace('.', '_');
                builder.insert(0, "]");
                builder.insert(0, value);
                builder.insert(0, "[");
            }

            builder.insert(0, current.getName());
        }

        return builder.toString();
    }

    public String evalString(String expression) {
        return this.xpathParser.evalString(this.node, expression);
    }

    public Boolean evalBoolean(String expression) {
        return this.xpathParser.evalBoolean(this.node, expression);
    }

    public Double evalDouble(String expression) {
        return this.xpathParser.evalDouble(this.node, expression);
    }

    public List<XNode> evalNodes(String expression) {
        return this.xpathParser.evalNodes(this.node, expression);
    }

    public XNode evalNode(String expression) {
        return this.xpathParser.evalNode(this.node, expression);
    }

    public Node getNode() {
        return this.node;
    }

    public String getName() {
        return this.name;
    }

    public String getStringBody() {
        return this.getStringBody((String)null);
    }

    public String getStringBody(String def) {
        return this.body == null ? def : this.body;
    }

    public Boolean getBooleanBody() {
        return this.getBooleanBody((Boolean)null);
    }

    public Boolean getBooleanBody(Boolean def) {
        return this.body == null ? def : Boolean.valueOf(this.body);
    }

    public Integer getIntBody() {
        return this.getIntBody((Integer)null);
    }

    public Integer getIntBody(Integer def) {
        return this.body == null ? def : Integer.parseInt(this.body);
    }

    public Long getLongBody() {
        return this.getLongBody((Long)null);
    }

    public Long getLongBody(Long def) {
        return this.body == null ? def : Long.parseLong(this.body);
    }

    public Double getDoubleBody() {
        return this.getDoubleBody((Double)null);
    }

    public Double getDoubleBody(Double def) {
        return this.body == null ? def : Double.parseDouble(this.body);
    }

    public Float getFloatBody() {
        return this.getFloatBody((Float)null);
    }

    public Float getFloatBody(Float def) {
        return this.body == null ? def : Float.parseFloat(this.body);
    }

    public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name) {
        return this.getEnumAttribute(enumType, name, (T) null);
    }

    public <T extends Enum<T>> T getEnumAttribute(Class<T> enumType, String name, T def) {
        String value = this.getStringAttribute(name);
        return value == null ? def : Enum.valueOf(enumType, value);
    }

    public String getStringAttribute(String name) {
        return this.getStringAttribute(name, (String)null);
    }

    public String getStringAttribute(String name, String def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : value;
    }

    public Boolean getBooleanAttribute(String name) {
        return this.getBooleanAttribute(name, (Boolean)null);
    }

    public Boolean getBooleanAttribute(String name, Boolean def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : Boolean.valueOf(value);
    }

    public Integer getIntAttribute(String name) {
        return this.getIntAttribute(name, (Integer)null);
    }

    public Integer getIntAttribute(String name, Integer def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : Integer.parseInt(value);
    }

    public Long getLongAttribute(String name) {
        return this.getLongAttribute(name, (Long)null);
    }

    public Long getLongAttribute(String name, Long def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : Long.parseLong(value);
    }

    public Double getDoubleAttribute(String name) {
        return this.getDoubleAttribute(name, (Double)null);
    }

    public Double getDoubleAttribute(String name, Double def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : Double.parseDouble(value);
    }

    public Float getFloatAttribute(String name) {
        return this.getFloatAttribute(name, (Float)null);
    }

    public Float getFloatAttribute(String name, Float def) {
        String value = this.attributes.getProperty(name);
        return value == null ? def : Float.parseFloat(value);
    }

    public List<XNode> getChildren() {
        List<XNode> children = new ArrayList();
        NodeList nodeList = this.node.getChildNodes();
        if (nodeList != null) {
            int i = 0;

            for(int n = nodeList.getLength(); i < n; ++i) {
                Node node = nodeList.item(i);
                if (node.getNodeType() == 1) {
                    children.add(new XNode(this.xpathParser, node, this.variables));
                }
            }
        }

        return children;
    }

    public Properties getChildrenAsProperties() {
        Properties properties = new Properties();
        Iterator var2 = this.getChildren().iterator();

        while(var2.hasNext()) {
            XNode child = (XNode)var2.next();
            String name = child.getStringAttribute("name");
            String value = child.getStringAttribute("value");
            if (name != null && value != null) {
                properties.setProperty(name, value);
            }
        }

        return properties;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        this.toString(builder, 0);
        return builder.toString();
    }

    private void toString(StringBuilder builder, int level) {
        builder.append("<");
        builder.append(this.name);
        Iterator var3 = this.attributes.entrySet().iterator();

        while(var3.hasNext()) {
            Entry<Object, Object> entry = (Entry)var3.next();
            builder.append(" ");
            builder.append(entry.getKey());
            builder.append("=\"");
            builder.append(entry.getValue());
            builder.append("\"");
        }

        List<XNode> children = this.getChildren();
        if (!children.isEmpty()) {
            builder.append(">\n");

            for(int k = 0; k < children.size(); ++k) {
                this.indent(builder, level + 1);
                ((XNode)children.get(k)).toString(builder, level + 1);
            }

            this.indent(builder, level);
            builder.append("</");
            builder.append(this.name);
            builder.append(">");
        } else if (this.body != null) {
            builder.append(">");
            builder.append(this.body);
            builder.append("</");
            builder.append(this.name);
            builder.append(">");
        } else {
            builder.append("/>");
            this.indent(builder, level);
        }

        builder.append("\n");
    }

    private void indent(StringBuilder builder, int level) {
        for(int i = 0; i < level; ++i) {
            builder.append("    ");
        }

    }

    private Properties parseAttributes(Node n) {
        Properties attributes = new Properties();
        NamedNodeMap attributeNodes = n.getAttributes();
        if (attributeNodes != null) {
            for(int i = 0; i < attributeNodes.getLength(); ++i) {
                Node attribute = attributeNodes.item(i);
//                String value = PropertyParser.parse(attribute.getNodeValue(), this.variables);
//                attributes.put(attribute.getNodeName(), value);
            }
        }

        return attributes;
    }

    private String parseBody(Node node) {
        String data = this.getBodyData(node);
        if (data == null) {
            NodeList children = node.getChildNodes();

            for(int i = 0; i < children.getLength(); ++i) {
                Node child = children.item(i);
                data = this.getBodyData(child);
                if (data != null) {
                    break;
                }
            }
        }

        return data;
    }

    private String getBodyData(Node child) {
        if (child.getNodeType() != 4 && child.getNodeType() != 3) {
            return null;
        } else {
            String data = ((CharacterData)child).getData();
//            data = PropertyParser.parse(data, this.variables);
            return data;
        }
    }
}

